Repository navigation
fix(sona): stop JS input from aborting Node or poisoning learned state (@ruvector/sona 0.1.9, ruvector-sona 0.2.2) - #1322
Merged
Merged
Conversation
@ruvector/sona 0.1.8 aborts the whole Node process (SIGABRT, uncatchable)
on ordinary bad input, because a Rust panic cannot unwind through NAPI:
- applyMicroLora(x) with x.length != hiddenDim (assert_eq in MicroLoRA)
- applyBaseLora(i, x) with x.length > hiddenDim (index out of bounds)
- withConfig({ microLoraRank: 0 | 3 }) (assert in MicroLoRA::new)
These now throw JS errors. Also fixed in the core crate:
- One trajectory with NaN/Inf quality, reward, embedding or activations
left the instant-loop EWC++ Fisher permanently NaN and, after
forceLearn, made every base-LoRA output NaN.
Non-finite trajectories are now dropped at InstantLoop::on_trajectory.
- BaseLoRA::forward_layer is a no-op for rank 0 (scale was 0/0 = NaN)
and for input/output not hiddenDim long (was an OOB panic).
- Trajectory builders were one process-global map: any engine could end
another engine's trajectory, and unended ones leaked for the process
lifetime. They are now per engine.
Bumps ruvector-sona 0.2.1 -> 0.2.2 and @ruvector/sona 0.1.8 -> 0.1.9,
and realigns the committed optionalDependencies / npm/* versions (0.1.5)
with the published ones.
Co-Authored-By: claude-flow <ruv@ruv.net>
ruvnet
force-pushed
the
fix/sona-review-2026-11-04
branch
from
November 4, 2026 06:57
b689dfc to
b9d62a5
Compare
The unpinned install now resolves 3.10.x, whose --use-napi-cross setup fails on aarch64-unknown-linux-gnu with 'Cannot find native binding' (reproduced twice on this PR). 3.7.3 is what the last green run (2026-07-17) used; pinning keeps the old-glibc napi-cross toolchain. Co-Authored-By: claude-flow <ruv@ruv.net>
…-napi-cross --use-napi-cross still fails with @napi-rs/cli pinned to 3.7.3 (same 'Cannot find native binding' while setting up the toolchain), so the CLI version was not the cause. Use the gcc-aarch64-linux-gnu linker, as build-diskann.yml does, on ubuntu-22.04. Verified locally: produces an aarch64 ELF sona.linux-arm64-gnu.node, highest symbol GLIBC_2.34 (Ubuntu 22.04+, Debian 12, RHEL 9). Co-Authored-By: claude-flow <ruv@ruv.net>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
A review of
@ruvector/sona0.1.8 (306k downloads a month) found inputs from JavaScript that abort the Node process. A Rust panic cannot unwind through NAPI, so the process gets SIGABRT ("fatal runtime error: failed to initiate panic, error 5, aborting") and the caller cannot catch it. The review also found that a single NaN can silently ruin the learned state.new SonaEngine(64).applyMicroLora([1,2,3])input length 3 does not match hiddenDim 64new SonaEngine(8).applyBaseLora(0, Array(20))SonaEngine.withConfig({hiddenDim:64, microLoraRank:3 or 0})microLoraRank must be 1 or 2withConfig({baseLoraRank:0}).applyBaseLora(...)forceLearnevery base-LoRA output is NaNa.beginTrajectory(), thenb.endTrajectory(id)b, because the builder map was process-global and leaked any trajectory that was never endedOn microLoraRank, I chose to throw an error rather than clamp, to match the
(1-2)contract inindex.d.ts. The return types are nownapi::Result<T>, which leaves the generated TS signatures unchanged.Tests (red first, then green)
crates/sona/tests/robustness.rs: 4 new tests. All 4 failed on HEAD and all 4 pass now.npm/packages/sona/test/basic.test.js: 5 new tests. On HEAD the whole file died withsignal: 'SIGABRT'. Against a locally built binary it now passes 19/19.cargo test -p ruvector-sona: 114 + 4 pass.cargo clippy -p ruvector-sona --all-features --all-targets -D warningsand the same with--features napiare both clean.Versions and release notes
ruvector-sona0.2.1 → 0.2.2. crates.io 0.2.1 dates from 2026-06-27, so it also lacks fix: SONA zero-vector bug, RVF wasm persistence, metadata safety (#704, #705, #706, #707) #708 and fix(sona): make MicroLoRA learn from feedback and make EWC++ take effect #1258.@ruvector/sona0.1.8 → 0.1.9. The committedoptionalDependenciesandnpm/*/package.jsonwere at 0.1.5, while the published package pins 0.1.8.sona-napi.ymlrewrites them at publish time, but they are now realigned in the repo..nodefiles predate fix(sona): make MicroLoRA learn from feedback and make EWC++ take effect #1258 (the MicroLoRA/EWC++ learning fix), so npm users don't have it yet.sona-napi.ymlby pushing asona-v0.1.9tag or byworkflow_dispatchwithpublish=true. It uses theNPM_TOKENGitHub secret.Not changed (reported only)
forward_simdpath is never compiled in: the published x64 binary contains 0vfmaddinstructions, soenableSimddoes nothing.beginTrajectorywith an embedding whose length differs from hiddenDim makes micro-LoRA skip the update silently.hiddenDim,trajectoryCapacityorpatternClustersvalues make the allocation abort.src/napi.rsis dead code: it is not in the module tree.wasm.rsandruvector-pycall the same asserting coreapply_micro_lorawithout checking the length.🤖 Generated with claude-flow